package com.lan.workflow.aspect;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.lan.workflow.annotation.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;


@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private ObjectMapper objectMapper;

    // 配置织入点
    @Pointcut("@annotation(com.lan.workflow.annotation.Log)")
    public void logPointCut() {

    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {

    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    @AfterReturning(value = "logPointCut()", returning = "returnMsg")
    public void doAfterReturning(JoinPoint joinPoint, Object returnMsg) {
        handleLog(joinPoint, null, returnMsg);
    }


    protected void handleLog(final JoinPoint joinPoint, final Exception e, final Object returnMsg) {
        try {
            // 获得注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            ObjectNode objectNode = objectMapper.createObjectNode();
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

            if (e != null) {
                StringBuffer errorBuffer = new StringBuffer(e.getClass().getName()).append("\r\n").append(e.getMessage());
                for (StackTraceElement element : e.getStackTrace()) {
                    errorBuffer.append("\r\n\t").append(element);
                }
                objectNode.put("errorMessage", errorBuffer.toString());
            }
            if(returnMsg != null){
                objectNode.putPOJO("response", returnMsg);
            }


            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            objectNode.put("method", className + "." + methodName + "()");

            Object[] args = joinPoint.getArgs();
            if(args != null && args.length > 0){
                objectNode.putPOJO("param", args[0]);
            }

            objectNode.putPOJO("requestParam", request.getParameterMap());
            log.info("{} " + controllerLog.value() + " {}",request.getRequestURI(), objectNode.toString());
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("日志通知异常:", exp);
        }
    }



    /**
     * 是否存在注解，如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }
}
